Program to create bootcache area Changes to bootcache program to match changes that have been made to the bootcache device mapper. BUG=chromium-os:25441 TEST=created bootcache and booted system. Change-Id: Idc62d4cea72d69f4c202460400219184916231ee Reviewed-on: https://gerrit.chromium.org/gerrit/31761 Reviewed-by: Luigi Semenzato <semenzato@chromium.org> Commit-Ready: Paul Taysom <taysom@chromium.org> Tested-by: Paul Taysom <taysom@chromium.org>
diff --git a/bootcache.c b/bootcache.c index 515bde2..5fa9a8f 100644 --- a/bootcache.c +++ b/bootcache.c
@@ -12,7 +12,10 @@ * conjunction with dm-bootcache device mapper to coalesce * the blocks used during boot. * - * bootcache [-t] <device-name> + * Sizes and offsets are measured in 512 byte sectors. + * Space is allocated in CHUNK_SIZE chunks. + * + * bootcache [-t] <device-name> <raw-partition> * * -t - for testing - looks in a different place for * information files. @@ -21,14 +24,14 @@ * prefix. * * Files: - * 1. Device - /dev/dm-0 - Where the blocks to be cached - * are stored. Both the original and cached - * copy. + * 1. Device - <raw-partition> - Where the blocks to be + * cached are stored. Both the original and + * cached copy. * 2. Header - /sys/kernel/debug/dm-bootcache/dm-0/header * Header for the boot cache. It contains the * information the bootcache utility will need * to create the bootcache. - * 3. Trace - /sys/kernel/debug/dm-bootcache/dm-0/trace + * 3. Trace - /sys/kernel/debug/dm-bootcache/dm-0/blocktrace * Trace of files read during boot * 4. Valid - /sys/kernel/debug/dm-bootcache/dm-0/valid * Returns "1" if cache is valid @@ -41,14 +44,16 @@ #include <sys/types.h> #include <sys/stat.h> +#include <sys/user.h> #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <stdbool.h> -#include <stdlib.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <syslog.h> #include <unistd.h> #include "dm-bootcache.h" @@ -56,30 +61,41 @@ typedef uint64_t u64; typedef uint32_t u32; -#define MAX_BLOCKS 128 +#define CHUNK_SIZE 4096 /* todo(taysom) should get from bootcache_hdr */ +#define SECTOR_SHIFT 9 +#define MAX_CHUNKS 128 #define MAX_FILE_NAME 256 +#define SECTORS_PER_CHUNK (CHUNK_SIZE >> SECTOR_SHIFT) +#define MAX_MSG 1024 -static struct Bootcache_hdr Header; +static struct bootcache_hdr Header; static struct { - struct Trace *tr; + struct bootcache_trace *tr; int num; } Trace; static const char Progname[] = "bootcache"; -static char Device_file[MAX_FILE_NAME]; static char Valid_file[MAX_FILE_NAME]; static char Free_file[MAX_FILE_NAME]; static char Header_file[MAX_FILE_NAME]; -static char Trace_file[MAX_FILE_NAME]; +static char Blocktrace_file[MAX_FILE_NAME]; -static u64 Header_block; static u64 Trace_start; static u64 Cache_start; #define fatal(fmt, ...) pr_fatal(__FILE__, __FUNCTION__, __LINE__, \ fmt, ## __VA_ARGS__) +#define PRs(_x) printf("|%s<%d> %s %s\n", __FUNCTION__, __LINE__, \ + # _x, _x); + +#define PRd(_x) printf("|%s<%d> %s %lld\n", __FUNCTION__, __LINE__, \ + # _x, (unsigned long long)(_x)); + +#define PRx(_x) printf("|%s<%d> %s %llx\n", __FUNCTION__, __LINE__, \ + # _x, (unsigned long long)(_x)); + /* pr_fatal: print error message and exit */ static void pr_fatal( const char *file, @@ -87,20 +103,28 @@ int line, const char *fmt, ...) { + char msg[MAX_MSG]; va_list args; + int n = MAX_MSG; + int i = 0; + int r; fflush(stdout); - fprintf(stderr, "Fatal %s %s:%s<%d> ", Progname, file, func, line); - if (fmt) { + r = snprintf(msg, n, "Fatal %s %s:%s<%d> ", Progname, file, func, line); + n -= r; + i += r; + if (n && fmt) { va_start(args, fmt); - vfprintf(stderr, fmt, args); + r = vsnprintf(&msg[i], n, fmt, args); + n -= r; + i += r; va_end(args); - if (fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { - fprintf(stderr, " %s<%d>", strerror(errno), errno); + if (n && fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { + snprintf(&msg[i], n, " %s<%d>", strerror(errno), errno); } } - fprintf(stderr, "\n"); + syslog(LOG_ERR, "%s\n", msg); exit(2); /* conventional value for failed execution */ } @@ -153,14 +177,14 @@ void *buf; int rc; - rc = posix_memalign(&buf, BLK_SIZE, npages * BLK_SIZE); + rc = posix_memalign(&buf, CHUNK_SIZE, npages * CHUNK_SIZE); if (rc) { fatal("posix_memalign rc=%d", rc); } return buf; } -static u64 num_blocks_in_cache(void) +static u64 num_sectors_in_cache(void) { int i; u64 sum = 0; @@ -171,33 +195,41 @@ return sum; } -static void compute_sections(void) +static u64 num_meta_sectors(void) { - Header.num_blks_meta = BLK_ALIGN(Trace.num * sizeof(*Trace.tr)); - Header.num_blks_data = num_blocks_in_cache(); - Header_block = Header.blkno; - Trace_start = Header_block + 1; - Cache_start = Trace_start + Header.num_blks_meta; + u64 num_bytes = Trace.num * sizeof(*Trace.tr); + + /* Align to page boundary then convert to sectors */ + return ((num_bytes + CHUNK_SIZE - 1) / CHUNK_SIZE) * SECTORS_PER_CHUNK; } -static void copy_trace(int dst, int src, struct Trace tr, void *buf) +static void compute_sections(void) +{ + Header.num_trace_recs = Trace.num; + Header.sectors_meta = num_meta_sectors(); + Header.sectors_data = num_sectors_in_cache(); + Trace_start = Header.sector + SECTORS_PER_CHUNK; + Cache_start = Trace_start + Header.sectors_meta; +} + +static void copy_trace(int dst, int src, struct bootcache_trace tr, void *buf) { u64 n; u64 remainder; u64 offset; int rc; - offset = tr.blkno << BLK_SHIFT; - remainder = tr.count << BLK_SHIFT; - n = BLK_SIZE * MAX_BLOCKS; + offset = tr.sector << SECTOR_SHIFT; + remainder = tr.count << SECTOR_SHIFT; + n = MAX_CHUNKS * CHUNK_SIZE; while (remainder) { if (n > remainder) { n = remainder; } rc = pread(src, buf, n, offset); if (rc < 0) { - fatal("pread trace offset=%llu num blocks=%llu", - offset >> BLK_SHIFT, n >> BLK_SHIFT); + fatal("pread trace offset=%llu num sectors=%llu:", + offset >> SECTOR_SHIFT, n >> SECTOR_SHIFT); } if (rc != n) { fatal("pread read only %u bytes expected %llu", @@ -205,8 +237,8 @@ } rc = write(dst, buf, n); if (rc < 0) { - fatal("write trace offset=%llu num blocks=%llu", - offset >> BLK_SHIFT, n >> BLK_SHIFT); + fatal("write trace offset=%llu num sectors=%llu:", + offset >> SECTOR_SHIFT, n >> SECTOR_SHIFT); } if (rc != n) { fatal("write wrote only %u bytes expected %llu", @@ -224,8 +256,9 @@ int src = open(device, O_RDONLY); int dst = open(device, O_WRONLY); - void *buf = malloc_buf(MAX_BLOCKS); - rc = lseek(dst, Cache_start << BLK_SHIFT, SEEK_SET); + void *buf = malloc_buf(MAX_CHUNKS); + + rc = lseek(dst, Cache_start << SECTOR_SHIFT, SEEK_SET); if (rc == -1) { fatal("lseek for cache start:"); } @@ -238,28 +271,31 @@ eclose(src); } -static void dump_trace(struct Trace *tr, int num_recs) +static void dump_trace() { + struct bootcache_trace *tr = Trace.tr; int i; - for (i = 0; i < num_recs; i++, tr++) { - printf("%llu %llu %llu\n", tr->blkno, tr->count, tr->ino); + if (0) { + for (i = 0; i < Trace.num; i++, tr++) { + printf("%llu %llu %llu\n", tr->sector, tr->count, tr->ino); + } } } /* - * Because we are reading a pseudo file, we scan it to - * see how big it is. + * Because we are reading a pseudo file in sysfs, + * we scan it to see how big it is. */ -static u64 num_traces(const char *file) +static u64 num_bytes(const char *file) { - struct Trace trace[1024]; + char buf[CHUNK_SIZE]; ssize_t rc; u64 sum = 0; int fd = eopen(file, O_RDONLY); for (;;) { - rc = read(fd, trace, sizeof(trace)); + rc = read(fd, buf, sizeof(buf)); if (rc == -1) fatal("read %s:", file); if (rc == 0) @@ -267,23 +303,39 @@ sum += rc; } eclose(fd); - return sum / sizeof(struct Trace); + return sum; } static void read_trace(const char *file) { - u64 n = num_traces(file); + /* + * Because this is a sysfs file, we have to read it to get + * its size. Even if more data is appended to the file, we + * don't care, we just want the data up to this point in + * time. + */ + u64 n = num_bytes(file); ssize_t rc; int fd; + char *b; - Trace.tr = emalloc(n * sizeof(struct Trace)); - Trace.num = n; + Trace.tr = emalloc(n); + Trace.num = n / sizeof(struct bootcache_trace); fd = eopen(file, O_RDONLY); - rc = read(fd, Trace.tr, n * sizeof(struct Trace)); - if (rc == -1) { - fatal("read %s:", file); + /* + * Because sysfs only returns a page at a time, + * will need to do the read in a loop. + */ + for (b = (char *)Trace.tr; n; n -= rc, b += rc) { + rc = read(fd, b, n); + if (rc == -1) { + fatal("read %s:", file); + } + if (rc == 0) { + fatal("trying to read %lld bytes", n); + } } - dump_trace(Trace.tr, n); + dump_trace(); eclose(fd); } @@ -319,7 +371,7 @@ int rc; fd = eopen(file, O_WRONLY); - rc = pwrite(fd, &Header, sizeof(Header), Header_block << BLK_SHIFT); + rc = pwrite(fd, &Header, sizeof(Header), Header.sector << SECTOR_SHIFT); if (rc != sizeof(Header)) { fatal("pwrite %s rc=%d:", file, rc); } @@ -334,7 +386,7 @@ ssize_t size = Trace.num * sizeof(*Trace.tr); fd = eopen(file, O_WRONLY); - rc = pwrite(fd, Trace.tr, size, Trace_start); + rc = pwrite(fd, Trace.tr, size, Trace_start << SECTOR_SHIFT); if (rc != size) { fatal("pwrite %s rc=%ld size=%ld:", file, rc, size); } @@ -380,96 +432,77 @@ return buf[0] == '1'; } -static void gen_file_name(char *file_name, int size, - const char *fmt, const char *name) +static void gen_file_name(char *file_name, int size, const char *fmt, + const char *prefix, const char *name) { int rc; - rc = snprintf(file_name, size, fmt, name); + rc = snprintf(file_name, size, fmt, prefix, name); if (rc >= size) { fatal("Name too long %s", name); } } -static void gen_file_names(const char *name) +static void gen_file_names(const char *fmt, const char *device_mapper) { - gen_file_name(Device_file, sizeof(Device_file), "/dev/%s", name); gen_file_name(Valid_file, sizeof(Valid_file), - "/sys/kernel/debug/dm-bootcache/%s/valid", name); + fmt, device_mapper, "valid"); gen_file_name(Free_file, sizeof(Free_file), - "/sys/kernel/debug/dm-bootcache/%s/free", name); + fmt, device_mapper, "free"); gen_file_name(Header_file, sizeof(Header_file), - "/sys/kernel/debug/dm-bootcache/%s/header", name); - gen_file_name(Trace_file, sizeof(Trace_file), - "/sys/kernel/debug/dm-bootcache/%s/trace", name); -} - -static void test_file_names(const char *name) -{ - gen_file_name(Device_file, sizeof(Device_file), - "/tmp/%s/dev", name); - gen_file_name(Valid_file, sizeof(Valid_file), - "/tmp/%s/valid", name); - gen_file_name(Free_file, sizeof(Free_file), - "/tmp/%s/free", name); - gen_file_name(Header_file, sizeof(Header_file), - "/tmp/%s/header", name); - gen_file_name(Trace_file, sizeof(Trace_file), - "/tmp/%s/trace", name); + fmt, device_mapper, "header"); + gen_file_name(Blocktrace_file, sizeof(Blocktrace_file), + fmt, device_mapper, "blocktrace"); } static void usage(void) { - fprintf(stderr, "Usage: %s [-t] <name>\n" - " e.g %s dm-0\n", + fprintf(stderr, "Usage: %s [-t]" + " <device mapper> <raw partition>\n" + " e.g %s dm-0 /dev/sda3\n", Progname, Progname); exit(2); } int main(int argc, char *argv[]) { - bool test = false; - char *name = NULL; + char *device_mapper = NULL; + char *raw_partition = NULL; + openlog(Progname, LOG_PERROR | LOG_CONS | LOG_PID, 0); + syslog(LOG_ERR, "started\n"); for (;;) { int c; - c = getopt(argc, argv, "t?"); + c = getopt(argc, argv, "?"); if (c == -1) break; switch (c) { - case 't': - test = true; - break; case '?': default: usage(); break; } } - if (optind >= argc) { + if (optind+2 != argc) { usage(); } - name = argv[optind]; - if (test) { - test_file_names(name); - } else { - gen_file_names(name); - } - if (is_valid(Valid_file)) { - /* Because the boot cache is valid, the block - * traces are not kept so the boot cache can't - * be rebuilt. To force a rebuild of the cache, - * zero the header. + device_mapper = argv[optind]; + raw_partition = argv[optind + 1]; + gen_file_names("/sys/devices/virtual/block/%s/dm/%s", + device_mapper); + if (!is_valid(Valid_file)) { + /* + * Rebuild the bootcache */ - return 0; + read_header(Header_file); + read_trace(Blocktrace_file); + compute_sections(); + copy_blocks(raw_partition); + write_trace(raw_partition); + write_header(raw_partition); } - read_header(Header_file); - read_trace(Trace_file); - compute_sections(); - copy_blocks(Device_file); - write_trace(Device_file); - write_header(Device_file); free_bootcache(Free_file); + syslog(LOG_ERR, "done\n"); return 0; } diff --git a/bootcache.conf b/bootcache.conf new file mode 100644 index 0000000..27d00db --- /dev/null +++ b/bootcache.conf
@@ -0,0 +1,19 @@ +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Start the bootcache process" +author "chromium-os-dev@chromium.org" + +# Starts the bootcache deamon that either creates the bootcache +# or cleans it up after it is used. +start on started system-services + +pre-start script + sleep 5 # Give a little extra time for chrome +end script + +script + device=$(rootdev -s) + exec ionice -c3 bootcache dm-0 $device +end script
diff --git a/dm-bootcache.h b/dm-bootcache.h index af12df8..536911f 100644 --- a/dm-bootcache.h +++ b/dm-bootcache.h
@@ -1,42 +1,44 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. +/* + * Copyright 2012 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ -#ifndef _BOOTCACHE_H -#define _BOOTCACHE_H +#ifndef DM_BOOTCACHE_H +#define DM_BOOTCACHE_H #include <linux/types.h> -enum { BLK_SHIFT = 12, - BLK_SIZE = 1<<BLK_SHIFT, - BLK_MASK = BLK_SIZE - 1, - SECTOR_BLOCK_SHIFT = 3, - BOOTCACHE_MAGIC = 1651470196, +enum { BOOTCACHE_MAGIC = 1651470196, BOOTCACHE_VERSION = 2, MAX_SIGNATURE = 256 }; - -#define BLK_ALIGN(_x) (((_x) + BLK_MASK) >> BLK_SHIFT) - -struct Trace { - __u64 blkno; /* Block number for data */ - __u64 count; /* Number of blocks in request */ - __u64 ino; /* Used for anaysis of traces */ +struct bootcache_trace { + __u64 sector; /* Sector offset */ + __u64 count; /* Number of blocks traced */ + __u64 ino; /* Inode number of file */ }; -struct Bootcache_hdr { - __u64 blkno; /* Block number for where header is stored */ +struct bootcache_hdr { + __u64 sector; /* Sector offset where header is stored */ __u32 magic; /* Magic number */ __u32 version; /* Verion of boot cache */ __u32 state; /* Curent state */ - __u32 num_blks_meta; /* Size of trace data on disk */ - __u32 num_blks_data; /* Size of the data area */ - char date[12]; /* Date and time bootcache was compiled */ - char time[12]; + __u32 num_trace_recs; /* Number of trace reords */ + __u32 sectors_meta; /* Size of trace data on disk in sectors*/ + __u32 sectors_data; /* Size of the data area in sectors*/ __u32 max_sectors; /* Max sectors that can to read */ __u32 max_hw_sectors; /* Max hardware sectore that can be read */ + char date[12]; /* Date and time dm-bootcache was compiled */ + char time[12]; char signature[MAX_SIGNATURE]; };